Skip to content

Comments

[red-knot] Fix disambiguate display for union types#16907

Merged
MichaReiser merged 3 commits intoastral-sh:mainfrom
MatthewMckee4:fix-callable-union-display
Mar 22, 2025
Merged

[red-knot] Fix disambiguate display for union types#16907
MichaReiser merged 3 commits intoastral-sh:mainfrom
MatthewMckee4:fix-callable-union-display

Conversation

@MatthewMckee4
Copy link
Contributor

Summary

When callables are displayed in unions, like:

from typing import Callable


def foo(x: Callable[[], int] | None):
    # red-knot: Revealed type is `() -> int | None` [revealed-type]
    reveal_type(x)

This leaves the type rather ambiguous, to fix this we can add parenthesis to callable type in union

Fixes #16893

Test Plan

Update callable annotations tests

@github-actions
Copy link
Contributor

github-actions bot commented Mar 22, 2025

mypy_primer results

Changes were detected when running on open source projects
isort (https://github.com/pycqa/isort)
- error[lint:call-non-callable] /tmp/mypy_primer/projects/isort/isort/sorting.py:120:34: Object of type `(str, /) -> Any | None` is not callable
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/isort/isort/sorting.py:120:34: Object of type `((str, /) -> Any) | None` is not callable
+ error[lint:invalid-assignment] /tmp/mypy_primer/projects/isort/isort/settings.py:749:9: Object of type `Literal[from_string]` is not assignable to `((str, /) -> Any) | type[Any]`
- error[lint:invalid-assignment] /tmp/mypy_primer/projects/isort/isort/settings.py:749:9: Object of type `Literal[from_string]` is not assignable to `(str, /) -> Any | type[Any]`
- error[lint:invalid-argument-type] /tmp/mypy_primer/projects/isort/isort/output.py:55:17: Object of type `(key) -> Unknown` cannot be assigned to parameter `key` of function `sort`; expected type `(str, /) -> Any | None`
+ error[lint:invalid-argument-type] /tmp/mypy_primer/projects/isort/isort/output.py:55:17: Object of type `(key) -> Unknown` cannot be assigned to parameter `key` of function `sort`; expected type `((str, /) -> Any) | None`
- error[lint:invalid-argument-type] /tmp/mypy_primer/projects/isort/isort/output.py:66:17: Object of type `(key) -> Unknown` cannot be assigned to parameter `key` of function `sort`; expected type `(str, /) -> Any | None`
+ error[lint:invalid-argument-type] /tmp/mypy_primer/projects/isort/isort/output.py:66:17: Object of type `(key) -> Unknown` cannot be assigned to parameter `key` of function `sort`; expected type `((str, /) -> Any) | None`
- error[lint:invalid-argument-type] /tmp/mypy_primer/projects/isort/isort/output.py:113:17: Object of type `partial` cannot be assigned to parameter `key` of function `sort`; expected type `(str, /) -> Any | None`
+ error[lint:invalid-argument-type] /tmp/mypy_primer/projects/isort/isort/output.py:113:17: Object of type `partial` cannot be assigned to parameter `key` of function `sort`; expected type `((str, /) -> Any) | None`
- error[lint:invalid-argument-type] /tmp/mypy_primer/projects/isort/isort/output.py:268:17: Object of type `(key) -> Unknown` cannot be assigned to parameter `key` of function `sort`; expected type `(str, /) -> Any | None`
+ error[lint:invalid-argument-type] /tmp/mypy_primer/projects/isort/isort/output.py:268:17: Object of type `(key) -> Unknown` cannot be assigned to parameter `key` of function `sort`; expected type `((str, /) -> Any) | None`

rich (https://github.com/Textualize/rich)
- error[lint:invalid-argument-type] /tmp/mypy_primer/projects/rich/tests/test_progress.py:292:60: Object of type `MockClock` cannot be assigned to parameter `get_time` of function `track`; expected type `() -> int | float | None`
+ error[lint:invalid-argument-type] /tmp/mypy_primer/projects/rich/tests/test_progress.py:292:60: Object of type `MockClock` cannot be assigned to parameter `get_time` of function `track`; expected type `(() -> int | float) | None`

pyinstrument (https://github.com/joerick/pyinstrument)
+ error[lint:invalid-argument-type] /tmp/mypy_primer/projects/pyinstrument/test/low_level/test_threaded.py:37:24: Object of type `CallCounter` cannot be assigned to parameter 1 (`target`) of function `setstatprofile`; expected type `((FrameType, str, Any, /) -> Any) | None`
- error[lint:invalid-assignment] /tmp/mypy_primer/projects/pyinstrument/test/fake_time_util.py:28:5: Object of type `@Todo | <bound method `get_time` of `FakeClock`>` is not assignable to attribute `timer_func` of type `() -> int | float | None`
+ error[lint:invalid-assignment] /tmp/mypy_primer/projects/pyinstrument/test/fake_time_util.py:28:5: Object of type `@Todo | <bound method `get_time` of `FakeClock`>` is not assignable to attribute `timer_func` of type `(() -> int | float) | None`
- error[lint:invalid-argument-type] /tmp/mypy_primer/projects/pyinstrument/test/low_level/test_threaded.py:37:24: Object of type `CallCounter` cannot be assigned to parameter 1 (`target`) of function `setstatprofile`; expected type `(FrameType, str, Any, /) -> Any | None`

@AlexWaygood AlexWaygood added the ty Multi-file analysis & type inference label Mar 22, 2025
Comment on lines +155 to +169
## Union

```py
from typing import Callable, Union

def _(
c: Callable[[Union[int, str]], int] | None,
d: None | Callable[[Union[int, str]], int],
e: None | Callable[[Union[int, str]], int] | int,
):
reveal_type(c) # revealed: ((int | str, /) -> int) | None
reveal_type(d) # revealed: None | ((int | str, /) -> int)
reveal_type(e) # revealed: None | ((int | str, /) -> int) | int
```

Copy link
Contributor

@InSyncWithFoo InSyncWithFoo Mar 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about intersection types?

def _(
    c: Intersection[Callable[[Union[int, str]], int], A],
    d: Intersection[A, Callable[[Union[int, str]], int]],
    e: Intersection[A, Callable[[Union[int, str]], int], B],
    f: Intersection[Not[Callable[[int, str], Intersection[int, str]]]]
):
    reveal_type(c)  # revealed: ((int | str, /) -> int) & A
    reveal_type(d)  # revealed: A & ((int | str, /) -> int)
    reveal_type(e)  # revealed: A & ((int | str, /) -> int) & B
    reveal_type(f)  # revealed: ~((int, str, /) -> int & str)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, right now revealed type of f is ~(int, str, /) -> int & str". We can probably do this in another PR?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think it's fine to do this in another PR.

Co-authored-by: Micha Reiser <micha@reiser.io>
@MichaReiser MichaReiser merged commit 92028ef into astral-sh:main Mar 22, 2025
23 checks passed
@MatthewMckee4 MatthewMckee4 deleted the fix-callable-union-display branch March 22, 2025 12:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[red-knot] Disambiguate display for union types

4 participants